﻿using Sustainalytics.DataLayer.Shared;
using Sustainalytics.Entities;
using Sustainalytics.Portfolios.BL.Model;

namespace Sustainalytics.Portfolios.BL
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Sustainalytics.Companies;
    using Sustainalytics.Utils;

    public class SystemPortfolioManagement : ISystemPortfolioManagement
    {
        private readonly ICompanyMatchingLogic _companyMatchingLogic;

        private readonly IPortfolioMatching _portfolioMatching;

        private readonly IRepositoryCollection<Portfolio> _portfolioCollection;

        private readonly IRepositoryCollection<PortfolioDetails> _portfolioDetailsCollection;

        public SystemPortfolioManagement(
            ICompanyMatchingLogic companyMatchingLogic,
            IPortfolioMatching portfolioMatching,
            IRepositoryCollection<Portfolio> portfolioCollection,
            IRepositoryCollection<PortfolioDetails> portfolioDetailsCollection)
        {
            this._companyMatchingLogic = companyMatchingLogic;
            this._portfolioMatching = portfolioMatching;
            this._portfolioCollection = portfolioCollection;
            this._portfolioDetailsCollection = portfolioDetailsCollection;
        }

        public void RemoveAllAutomaticSystemPortfolios()
        {
            this._portfolioCollection.DeleteWhere(x => x.IsAutomatic && x.IsReadonly);
            this._portfolioDetailsCollection.DeleteAll();
        }


        private bool NameAlreadyExists(Guid id,string name)
        {
            var portfolios =
                this._portfolioCollection.ReadWhere(
                    p =>
                    p.Id != id && p.Name == name);
            return !portfolios.IsNullOrEmpty();
        }


        private string RetryPolicy(Guid id, string name)
        {
            int retryTimes = 1;
            bool successful = false;
            string tryName = name;
            while (!successful && retryTimes <= 5)
            {
                if (this.NameAlreadyExists(id, tryName))
                {
                    tryName = string.Format("{0}_({1})", name, retryTimes);
                    retryTimes++;
                }
                else
                {
                    successful = true;
                }
            }

            return tryName;
        }


        public IDictionary<Guid, Portfolio> Add(SystemPortfolioInfo[] portfolios)
        {
            var result = new Dictionary<Guid, Portfolio>();

            foreach (var portfolio in portfolios)
            {
                var name = this.RetryPolicy(portfolio.Id, portfolio.Name);
                
                var systemPortfolio = new AutomaticSystemPortfolio(portfolio.Id, name, portfolio.AccountIds);

                var capitalIqIds = _companyMatchingLogic.FindCapitalIdIdsForCompanies(portfolio.CompanyIds);

                ICollection<PortfolioSecuritiesRow> details = this._portfolioMatching.GetMainSecurityForCompanies(capitalIqIds, null);

                if (!details.IsNullOrEmpty())
                {
                    var securities = new List<PortfolioDetails>();

                    foreach (var detail in details)
                    {
                        PortfolioSecurityModel security = detail.Security;
                        if (detail.IsValid)
                        {
                            security.PortfolioId = systemPortfolio.Id;
                            if (security.Id == Guid.Empty)
                            {
                                security.Id = Guid.NewGuid();
                            }

                            securities.Add(security.GetPortfolioDetails());
                        }
                    }

                    this._portfolioDetailsCollection.CreateOrUpdateBatch(securities);
                    systemPortfolio.CompanyCount = securities.Count;
                    this._portfolioCollection.Create(systemPortfolio);
                    result.Add(systemPortfolio.Id, systemPortfolio);
                }
            }

            return result;
        }

        public void UpdateManualSystemPortfolios(IDictionary<Guid, Portfolio> automaticPortfoliosInfomation)
        {
            IList<Portfolio> portfolioBatch;
            int skip = 0;

            do
            {
                portfolioBatch = this._portfolioCollection.ReadWhere(x => x.IsAutomatic && x.IsReadonly == false, skip, 100);
                foreach (var portfolio in portfolioBatch)
                {
                    var toUpdate = portfolio as ManualSystemPortfolio;
                    if (toUpdate != null)
                    {
                        var relatedUniversesList = GetRelatedPortfoliosList(automaticPortfoliosInfomation, toUpdate);
                        toUpdate.UpdateManualSystemPortfolioAssociatedUniverses(relatedUniversesList);
                        this._portfolioCollection.Update(toUpdate.Id, toUpdate);
                    }
                }

                skip += 100;
            }
            while (!portfolioBatch.IsNullOrEmpty());
        }

        public void RemoveAllEmptyManualSystemPortfolios()
        {
            this._portfolioCollection.DeleteWhere(x => x.CompanyCount == 0 && x.IsAutomatic && x.IsReadonly == false);
        }

        private static List<Portfolio> GetRelatedPortfoliosList(IDictionary<Guid, Portfolio> automaticPortfoliosInfomation, ManualSystemPortfolio toUpdate)
        {
            return toUpdate.RelatedUniverses.Where(k => automaticPortfoliosInfomation.ContainsKey(k.Id))
                .Select(k => automaticPortfoliosInfomation[k.Id])
                .ToList();
        }
    }
}